﻿using System;
using System.Collections;
using System.Collections.Generic;
using Dorm.Helpers;
using Dorm.Maps;
using Dorm.QueryObject;

namespace Dorm.Services
{
    public class DeleteService
    {
        protected DataAccess DataAccess { get; set; }
        protected List<string> DeleteBatch { get; set; }

        public DeleteService(DataAccess dataAccess)
        {
            DataAccess = dataAccess;
            DeleteBatch = new List<string>();
        }

        public void MapAndRun(object entity)
        {
            bool useImplicitTransaction = DataAccess.HasValidTransaction == false;

            try
            {
                if (useImplicitTransaction)
                    DataAccess.BeginTransaction();

                ManageEntityDelete(entity);
                
                DeleteBatch.Reverse();

                foreach (var deleteCommand in DeleteBatch)
                    DataAccess.ExecuteNonQuery(deleteCommand);

                if (useImplicitTransaction)
                    DataAccess.CommitTransaction();
            }
            catch (Exception)
            {
                if (useImplicitTransaction)
                    DataAccess.RollbackTransaction();

                throw;
            }
        }

        private void ManageEntityDelete(object entity)
        {
            var entityInfo = new EntityInfo(entity);
            var entityMaps = MapLocator.GetMap(entityInfo.EntityType);

            var deleteEntity = new Delete().From(entityInfo.TypeName).Where("ID = " + entityInfo.ID);
            DeleteBatch.Add(deleteEntity.ToString());

            foreach (var propertyToCascade in entityMaps.FindAll(m => m.Cascade))
            {
                if (propertyToCascade.MappingType == MappedProperty.MappingTypes.HasManyToMany)
                {
                    string manyToManyTableName = MappedProperty.GetManyToManyTableName(entityInfo.TypeName, propertyToCascade.PropertyType.Name);
                    var deleteRelations = new Delete().From(manyToManyTableName)
                        .Where(entityInfo.TypeName + "ID = " + entityInfo.ID);

                    DeleteBatch.Add(deleteRelations.ToString());
                }
                else
                {
                    var childrenToDelete = entityInfo.GetValue(propertyToCascade.PropertyName) as IEnumerable;
                    
                    if (childrenToDelete != null)
                    {
                        foreach (var childToDelete in childrenToDelete)
                            ManageEntityDelete(childToDelete);
                    }
                }
            }
        }
    }
}
